home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-02-12 | 16.4 KB | 493 lines | [TEXT/PJMM] |
- { wMenu Manager }
- { by Jim Matthews }
-
- UNIT wMenu;
-
- INTERFACE
-
- USES
- ROM85; { uses HGetState and HSetState }
-
- CONST
- mBarHeight = 20;
- betweenTitles = 15; { # of pixels between adjacent menu titles }
- invertOverlap = 10; { # of pixels to invert on each side of a menu title }
- noneHilited = -1; { value to store in wMenuBar.hilited if nothing hilited }
- menuTitleBit = 31; { mac-style bit offset for menu title bit in enableFlags }
-
- TYPE
- wMenuRec = RECORD
- mh : MenuHandle;
- titleRect : Rect;
- END;
- wMenuBar = RECORD
- numMenus : integer;
- hilited : integer;
- gp : GrafPtr;
- wMenus : ARRAY[0..0] OF wMenuRec;
- END;
- wMenuBarPtr = ^wMenuBar;
- wMenuBarHandle = ^wMenuBarPtr;
-
- FUNCTION wInitMenus (gp : GrafPtr) : wMenuBarHandle;
- PROCEDURE wInsertMenu (theMenuBar : wMenuBarHandle;
- theMenu : MenuHandle;
- beforeID : integer);
- PROCEDURE wDrawMenuBar (theMenuBar : wMenuBarHandle);
- PROCEDURE wDeleteMenu (theMenuBar : wMenuBarHandle;
- menuID : integer);
- PROCEDURE wClearMenuBar (theMenuBar : wMenuBarHandle);
-
- FUNCTION wMenuSelect (theMenuBar : wMenuBarHandle;
- startPt : Point) : longint;
- FUNCTION wMenuKey (theMenuBar : wMenuBarHandle;
- ch : char) : longint;
- PROCEDURE wHiliteMenu (theMenuBar : wMenuBarHandle;
- menuID : integer);
-
- IMPLEMENTATION
-
- {wInitMenus -- create a wMenuBar and associate it with a grafport}
- FUNCTION wInitMenus; { (gp : GrafPtr) : wMenuBarHandle; }
- TYPE
- intptr = ^Integer;
- VAR
- mbar : wMenuBarHandle;
- TopMenuItemP : intptr;
- BEGIN
- { Set low-mem global to fix menu display bug }
- TopMenuItemP := intptr($A0A);
- TopMenuItemP^ := mBarHeight;
-
- mbar := wMenuBarHandle(NewHandle(sizeof(wMenuBar)));
- mbar^^.numMenus := 0;
- mbar^^.hilited := noneHilited;
- mbar^^.gp := gp;
- wInitMenus := mbar;
- END; { wInitMenus }
-
- {wInsertMenu -- insert a menu into a defined wMenuBar, analogous to InsertMenu }
- PROCEDURE wInsertMenu; { (theMenuBar : wMenuBarHandle; }
- { theMenu : MenuHandle; }
- { beforeID : integer); }
- VAR
- newSize : Size;
- r : Rect;
- i, j : integer;
- titleWidth, oldSize, oldFont : integer;
- BEGIN
- newSize := sizeof(wMenuBar) + theMenuBar^^.numMenus * sizeof(wMenuRec);
- IF newSize > GetHandleSize(handle(theMenuBar)) THEN
- SetHandleSize(handle(theMenuBar), newSize);
- IF MemError = noErr THEN
- BEGIN
- oldSize := thePort^.txSize;
- oldFont := thePort^.txFont;
- TextSize(12);
- TextFont(systemFont);
- titleWidth := StringWidth(theMenu^^.menuData);
- TextSize(oldSize);
- TextFont(oldFont);
- i := 0;
- IF beforeID > 0 THEN { Insert the menu before a particular one? }
- BEGIN
- i := 0;
- WHILE (theMenuBar^^.wMenus[i].mh^^.menuID <> beforeID) AND (i < theMenuBar^^.numMenus) DO
- i := i + 1;
- IF i <> theMenuBar^^.numMenus THEN
- BEGIN
- FOR j := theMenuBar^^.numMenus DOWNTO i + 1 DO
- BEGIN
- theMenuBar^^.wMenus[j].mh := theMenuBar^^.wMenus[j - 1].mh;
- theMenuBar^^.wMenus[j].titleRect := theMenuBar^^.wMenus[j - 1].titleRect;
- OffsetRect(theMenuBar^^.wMenus[j].titleRect, titleWidth + betweenTitles, 0);
- END; { for loop -- copying menus back }
- END; { if there's a menu id = beforeID }
- END { if beforeID <> 0 }
- ELSE { if beforeID <= 0, put it after the rest }
- i := theMenuBar^^.numMenus;
- WITH theMenuBar^^.wMenus[i] DO
- BEGIN
- titleRect.top := 1;
- titleRect.bottom := mBarHeight - 1;
- IF i = 0 THEN
- titleRect.left := betweenTItles - invertOverlap
- ELSE
- titleRect.left := theMenuBar^^.wMenus[i - 1].titleRect.right + betweenTitles - 2 * invertOverlap;
- titleRect.right := titleRect.left + titleWidth + 2 * invertOverlap;
- mh := theMenu;
- END; { with theMenuBar^^.wMenus[i] }
- theMenuBar^^.numMenus := theMenuBar^^.numMenus + 1;
- END; { no MemError }
- END; { wInsertMenu }
-
- { wDrawMenuBar -- draw the wMenuBar, with appropriate highlighting }
- PROCEDURE wDrawMenuBar; { (theMenuBar : wMenuBarHandle); }
- VAR
- i : integer;
- r : Rect;
- oldPort : GrafPtr;
- oldSize, oldFont, oldMode : integer;
- oldStyle : Style;
- bmap, oldMap : BitMap;
- BEGIN
- GetPort(oldPort);
- SetPort(theMenuBar^^.gp);
- oldSize := thePort^.txSize;
- oldFont := thePort^.txFont;
- oldMode := thePort^.txMode;
- oldStyle := thePort^.txFace;
- TextSize(12);
- TextFont(systemFont);
- TextMode(srcOr);
- TextFace([]);
-
- SetRect(r, 0, 0, 10000, mBarHeight);
- EraseRect(r);
- MoveTo(0, mBarHeight - 1);
- Line(10000, 0);
-
- FOR i := 0 TO theMenuBar^^.numMenus - 1 DO
- BEGIN
- MoveTo(theMenuBar^^.wMenus[i].titleRect.left + invertOverlap, mBarHeight - 5);
- DrawString(theMenuBar^^.wMenus[i].mh^^.menuData);
-
- { gray-out disabled menu titles }
- IF NOT BitTst(@theMenuBar^^.wMenus[i].mh^^.enableFlags, menuTitleBit) THEN
- BEGIN
- r := theMenuBar^^.wMenus[i].titleRect;
- r.left := r.left + invertOverlap;
- r.right := r.right - invertOverlap;
- bmap.bounds := r;
- OffsetRect(bmap.bounds, -r.left, -r.top);
- IF (bmap.bounds.right MOD 16) <> 0 THEN
- bmap.rowBytes := 2 * ((bmap.bounds.right DIV 16) + 1)
- ELSE
- bmap.rowBytes := 2 * (bmap.bounds.right DIV 16);
- bmap.baseAddr := NewPtr(bmap.rowBytes * mBarHeight);
- oldMap := thePort^.portBits;
- SetPortBits(bmap);
- FillRect(bmap.bounds, gray);
- SetPortBits(oldMap);
- HLock(handle(theMenuBar));
- CopyBits(bmap, thePort^.portBits, bmap.bounds, r, notSrcBic, NIL);
- HUnlock(handle(theMenuBar));
- DisposPtr(bmap.baseAddr);
- END; { if title disabled }
- END; { for each menu }
- IF theMenuBar^^.hilited <> noneHilited THEN
- InvertRect(theMenuBar^^.wMenus[theMenuBar^^.hilited].titleRect);
-
- TextSize(oldSize);
- TextFont(oldFont);
- TextMode(oldMode);
- TextFace(oldStyle);
- SetPort(oldPort);
- END; { wDrawMenuBar }
-
- {wDeleteMenu -- delete a menu from a wMenuBar}
- PROCEDURE wDeleteMenu; { (theMenuBar : wMenuBarHandle; }
- { menuID : integer); }
- VAR
- i, j, oldSize, oldFont : integer;
- oldStyle : Style;
- titleWidth : integer;
- BEGIN
- oldSize := thePort^.txSize; { reset the font/size/style to compute menu title widths }
- oldFont := thePort^.txFont;
- oldStyle := thePort^.txFace;
- TextSize(12);
- TextFont(systemFont);
- TextFace([]);
-
- i := 0;
- WHILE (theMenuBar^^.wMenus[i].mh^^.menuID <> menuID) AND (i < theMenuBar^^.numMenus) DO
- i := i + 1;
- IF i <> theMenuBar^^.numMenus THEN
- BEGIN
- titleWidth := StringWidth(theMenuBar^^.wMenus[i].mh^^.menuData);
- FOR j := i TO theMenuBar^^.numMenus - 1 DO
- BEGIN
- theMenuBar^^.wMenus[j].mh := theMenuBar^^.wMenus[j + 1].mh;
- theMenuBar^^.wMenus[j].titleRect := theMenuBar^^.wMenus[j + 1].titleRect;
- OffsetRect(theMenuBar^^.wMenus[j].titleRect, -(titleWidth + betweenTitles), 0);
- END; { for loop -- copying menus back }
- theMenuBar^^.numMenus := theMenuBar^^.numMenus - 1;
- END; { if there's a menu id = menuID }
-
- TextSize(oldSize);
- TextFont(oldFont);
- TextFace(oldStyle);
- END; { wDeleteMenu }
-
- {wClearMenuBar -- delete all the menus in a menu bar}
- PROCEDURE wClearMenuBar; { (theMenuBar : wMenuBarHandle); }
- BEGIN
- theMenuBar^^.numMenus := 0; { take the easy way out.... }
- END; { wClearMenuBar }
-
- {MenuDefProc -- inline call to the menu definition procedure}
- { Pop the address of the proc off the stack and jsr to it}
- PROCEDURE MenuDefProc (message : integer;
- theMenu : MenuHandle;
- VAR menuRect : Rect;
- hitPt : Point;
- VAR whichItem : integer;
- theProc : ProcPtr);
- INLINE
- $205F, $4E90; { pop.l A0 , jsr (A0) }
-
- {MenuDefGlue -- dereference the menu handle to find the address of}
- { the definition procedure and call it using MenuDefProc, above}
- PROCEDURE MenuDefGlue (message : integer;
- theMenu : MenuHandle;
- VAR menuRect : Rect;
- hitPt : Point;
- VAR whichItem : integer);
- BEGIN
- MenuDefProc(message, theMenu, menuRect, hitPt, whichItem, theMenu^^.menuProc^);
- END;
-
- {wMenuSelect -- pull down the menus and let the user select an item}
- FUNCTION wMenuSelect; { (theMenuBar : wMenuBarHandle; }
- { startPt : Point) : longint; }
- CONST
- flashDelay = 3; { # of ticks between calls to invert selected item }
- menuFrame = 2; { width of menu frame }
- MenuFlashAddr = $A24; { address of lo-mem global: # of times to flash menu }
- TYPE
- intPtr = ^integer;
- VAR
- bmap : BitMap;
- menuRect : Rect;
- oldClip : RgnHandle;
- nilPt : Point;
- blink, whichItem : integer;
- oldPort, wMgrPort : GrafPtr;
- i : integer;
- hstate, mprocState : SignedByte;
- ticks : longint;
- menuFlashP : intPtr;
- oldSize, oldFont, oldMode : integer;
- pnState : PenState;
- strayed : boolean;
- dummyEvt : EventRecord;
- BEGIN
- hState := HGetState(handle(theMenuBar));
- HLock(handle(theMenuBar));
- GetPort(oldPort);
- SetPort(theMenuBar^^.gp);
- oldSize := thePort^.txSize;
- oldFont := thePort^.txFont;
- oldMode := thePort^.txMode;
- TextSize(12);
- TextFont(systemFont);
- TextMode(srcOr);
- menuFlashP := intPtr(MenuFlashAddr);
- whichItem := 0;
- WHILE WaitMouseUp DO { loop while the mouse is down }
- BEGIN
- { find menu title that user is clicking on }
- i := theMenuBar^^.numMenus - 1;
- WHILE (i >= 0) AND NOT PtInRect(startPt, theMenuBar^^.wMenus[i].titleRect) DO
- i := i - 1;
-
- { if user is clicking on a menu title, have the menu "drop down" }
- IF i >= 0 THEN
- WITH theMenuBar^^.wMenus[i] DO { note: theMenuBar is locked }
- BEGIN
- wHiliteMenu(theMenuBar, mh^^.menuID); { hilite title }
- CalcMenuSize(mh); { calculate menu size, it may have changed }
- SetRect(menuRect, titleRect.left + 1, mBarHeight, titleRect.left + mh^^.menuWidth + 1, mBarHeight + mh^^.menuHeight);
- InsetRect(menuRect, -menuFrame, -menuFrame);
-
- { if the menu overlaps the edges of the window, trim it }
- IF menuRect.bottom > thePort^.portRect.bottom THEN
- menuRect.bottom := thePort^.portRect.bottom;
- IF menuRect.right > thePort^.portRect.right THEN
- OffsetRect(menuRect, thePort^.portRect.right - menuRect.right - 2, 0);
- IF menuRect.left < 0 THEN
- OffsetRect(menuRect, -menuRect.left, 0);
- bmap.rowBytes := ((menuRect.right - menuRect.left + 15) DIV 16) * 2;
- bmap.bounds := menuRect;
- bmap.baseAddr := NewPtr(bmap.rowBytes * (menuRect.bottom - menuRect.top));
- IF bmap.baseAddr <> NIL THEN { proceed if there is memory }
- BEGIN
- CopyBits(thePort^.portBits, bmap, bmap.bounds, bmap.bounds, srcCopy, NIL);
- oldClip := NewRgn;
- GetClip(oldClip);
- ClipRect(menuRect);
-
- { draw the menu -- thanks to Mike Schuster, MacTutor 12/85 }
- IF mh^^.menuHeight > 0 THEN
- BEGIN
- InsetRect(menuRect, menuFrame, menuFrame);
- EraseRect(menuRect);
- InsetRect(menuRect, -1, -1);
- FrameRect(menuRect);
- InsetRect(menuRect, 1, 1);
-
- GetPenState(pnState);
- PenNormal;
- MoveTo(menuRect.left + 1, menuRect.bottom + 1);
- Line((menuRect.right - menuRect.left), 0);
- Line(0, -(menuRect.bottom - menuRect.top));
- SetPenState(pnState);
- END; { if there're any menu items }
-
- LoadResource(mh^^.menuProc);
- mprocState := HGetState(handle(mh^^.menuProc));
- HLock(mh^^.menuProc);
- whichItem := 0;
- MenuDefGlue(mDrawMsg, mh, menuRect, startPt, whichItem);
-
- { send the mChooseMsg while the user is still in this menu }
- strayed := false;
- WHILE WaitMouseUp AND NOT strayed DO
- BEGIN
- MenuDefGlue(mChooseMsg, mh, menuRect, startPt, whichItem);
- GetMouse(startPt);
- strayed := (startPt.v < mBarHeight - 1) AND (startPt.v > 0) AND (NOT PtInRect(startPt, titleRect));
- IF i < theMenuBar^^.numMenus - 1 THEN
- strayed := strayed OR ((startPt.v < mBarHeight - 1) AND (startPt.v > 0) AND PtInRect(startPt, theMenuBar^^.wMenus[i + 1].titleRect));
-
- { Enable FKeys (i.e. screen dump) and DA updating }
- IF EventAvail(everyEvent, dummyEvt) THEN
- ;
- SystemTask;
- END; { while WaitMouseUp and not strayed }
-
- { flash the menu if an item was selected }
- IF (whichItem <> 0) AND NOT strayed THEN
- FOR blink := 1 TO menuFlashP^ DO
- BEGIN
- SetPt(nilPt, 0, 0);
- MenuDefGlue(mChooseMsg, mh, menuRect, nilPt, whichItem);
- Delay(flashDelay, ticks);
- MenuDefGlue(mChooseMsg, mh, menuRect, startPt, whichItem);
- Delay(flashDelay, ticks);
- END; { whichItem <> 0 }
- HSetState(mh^^.menuProc, mprocState);
- SetClip(oldClip);
- DisposeRgn(oldClip);
- CopyBits(bmap, thePort^.portBits, bmap.bounds, bmap.bounds, srcCopy, NIL);
- DisposPtr(bmap.baseAddr);
- END; { memory for bitmap }
- END { i >= 0: found the hit menu title }
- ELSE
- BEGIN { user isn't over a menu, so unhilite the last one hilited }
- IF theMenuBar^^.hilited <> noneHilited THEN
- wHiliteMenu(theMenuBar, 0);
- GetMouse(startPt); { need a new startPt -- mouse may have moved }
- END;{ no menu currently selected }
- END; { while WaitMouseUp -- looking for a hit menu title}
-
- { user let up on the mouse -- return the appropriate value }
- IF whichItem = 0 THEN
- wMenuSelect := 0
- ELSE
- wMenuSelect := BitShift(theMenuBar^^.wMenus[i].mh^^.menuID, 16) + whichItem;
- TextSize(oldSize);
- TextFont(oldFont);
- TextMode(oldMode);
- SetPort(oldPort);
- HSetState(handle(theMenuBar), hState);
- END; { wMenuSelect }
-
- {wMenuKey -- return the menu id and item no. with ch as it's cmd-key equivalent }
- { Caution: this assumes knowledge of the internal structure of MenuInfo.menuData }
- FUNCTION wMenuKey; { (theMenuBar : wMenuBarHandle; }
- { ch : char) : longint; }
- CONST
- flashDelay = 3;
- TYPE
- SignedBytePtr = ^SignedByte;
- CharPtr = ^char;
- VAR
- hState : SignedByte;
- bp, keyEquivP : SignedBytePtr;
- i, j, whichMenu, whichItem : integer;
- done, enabled : boolean;
- ticks : longint;
-
- { compare alphabetic characters w/o case sensitivity }
- FUNCTION equalChars (c1, c2 : char) : boolean;
- BEGIN
- IF c1 IN ['a'..'z'] THEN
- c1 := char(ord(c1) + ord('A') - ord('a'));
- IF c2 IN ['a'..'z'] THEN
- c2 := char(ord(c2) + ord('A') - ord('a'));
- equalChars := c1 = c2;
- END;
-
- BEGIN
- i := 0;
- done := false;
- WHILE (NOT done) AND (i < theMenuBar^^.numMenus) DO
- BEGIN
- hState := HGetState(handle(theMenuBar^^.wMenus[i].mh));
- HLock(handle(theMenuBar^^.wMenus[i].mh));
-
- { run down a menu, looking for an item w/ ch as its key equivalent }
- j := 1;
- bp := SignedBytePtr(@theMenuBar^^.wMenus[i].mh^^.menuData);
- bp := SignedBytePtr(ord4(bp) + bp^ + 1);
- WHILE (NOT done) AND (bp^ <> 0) DO
- BEGIN
- keyEquivP := SignedBytePtr(ord4(bp) + bp^ + 2);
- IF equalChars(ch, char(keyEquivP^)) THEN
- BEGIN
- whichMenu := theMenuBar^^.wMenus[i].mh^^.menuID;
- whichItem := j;
- done := true;
- END
- ELSE
- BEGIN
- j := j + 1;
- bp := SignedBytePtr(ord4(keyEquivP) + 3);
- END;
- END; { looking through this menu }
-
- HSetState(handle(theMenuBar^^.wMenus[i].mh), hState);
- i := i + 1;
- END; { while loop -- looking for key equiv }
- {the item is enabled if both it and its menu title are}
- enabled := BitTst(@theMenuBar^^.wMenus[i - 1].mh^^.enableFlags, menuTitleBit);
- enabled := enabled AND (j < 32) AND (BitTst(@theMenuBar^^.wMenus[i - 1].mh^^.enableFlags, menuTitleBit - j));
- IF done AND enabled THEN
- BEGIN
- wHiliteMenu(theMenuBar, whichMenu);
- wMenuKey := BitShift(whichMenu, 16) + whichItem;
- END { done }
- ELSE
- wMenuKey := 0;
- END; { wMenuKey }
-
- {wHiliteMenu -- unhilite the currently hilited menu title, and hilite a new one }
- { if menuID <> 0 }
- PROCEDURE wHiliteMenu; { (theMenuBar : wMenuBarHandle; }
- { menuID : integer); }
- VAR
- i : integer;
- oldPort : GrafPtr;
- BEGIN
- GetPort(oldPort);
- SetPort(theMenuBar^^.gp);
- IF (theMenuBar^^.hilited <> noneHilited) THEN
- InvertRect(theMenuBar^^.wMenus[theMenuBar^^.hilited].titleRect);
- theMenuBar^^.hilited := noneHilited;
- IF menuID <> 0 THEN
- BEGIN
- i := 0;
- WHILE (theMenuBar^^.wMenus[i].mh^^.menuID <> menuID) AND (i < theMenuBar^^.numMenus) DO
- i := i + 1;
- IF i <> theMenuBar^^.numMenus THEN
- BEGIN
- InvertRect(theMenuBar^^.wMenus[i].titleRect);
- theMenuBar^^.hilited := i;
- END; { found menuID }
- END; { menuID <> 0 }
- SetPort(oldPort);
- END; { wHiliteMenu }
-
- END.